agentmux_srv\backend\wshutil/
osc.rs1#![allow(dead_code)]
2use base64::Engine;
10
11pub const WAVE_OSC: &str = "23198";
13pub const WAVE_SERVER_OSC: &str = "23199";
15
16pub const BEL: u8 = 0x07;
18pub const ST: u8 = 0x9C;
20pub const ESC: u8 = 0x1B;
22
23pub const DEFAULT_OUTPUT_CH_SIZE: usize = 32;
25pub const DEFAULT_INPUT_CH_SIZE: usize = 32;
26
27const HEX_CHARS: &[u8] = b"0123456789ABCDEF";
29
30pub fn make_osc_prefix(osc_num: &str) -> Vec<u8> {
32 let mut prefix = Vec::with_capacity(3 + osc_num.len());
33 prefix.push(ESC);
34 prefix.push(b']');
35 prefix.extend_from_slice(osc_num.as_bytes());
36 prefix.push(b';');
37 prefix
38}
39
40pub fn encode_wave_osc_bytes(osc_num: &str, data: &[u8]) -> Result<Vec<u8>, String> {
47 if osc_num.len() != 5 {
48 return Err("osc_num must be 5 characters".to_string());
49 }
50
51 let needs_base64 = data.iter().any(|&b| b < 0x20 || b == 0x7F);
52
53 let prefix = make_osc_prefix(osc_num);
54 let payload = if needs_base64 {
55 base64::engine::general_purpose::STANDARD.encode(data).into_bytes()
56 } else {
57 data.to_vec()
58 };
59
60 let mut result = Vec::with_capacity(prefix.len() + payload.len() + 1);
61 result.extend_from_slice(&prefix);
62 result.extend_from_slice(&payload);
63 result.push(BEL);
64 Ok(result)
65}
66
67pub fn decode_wave_osc_bytes(data: &[u8]) -> Result<Vec<u8>, String> {
72 if data.len() < 9 {
73 return Err("data too short for OSC message".to_string());
74 }
75
76 if data[0] != ESC || data[1] != b']' {
78 return Err("invalid OSC prefix".to_string());
79 }
80
81 let sep_pos = data.iter().position(|&b| b == b';')
83 .ok_or("missing semicolon in OSC message")?;
84
85 let end_pos = data.len() - 1;
87 let terminator = data[end_pos];
88 if terminator != BEL && terminator != ST {
89 return Err(format!("invalid OSC terminator: 0x{:02X}", terminator));
90 }
91
92 let payload = &data[sep_pos + 1..end_pos];
93
94 if !payload.is_empty() && payload[0] == b'{' {
96 return Ok(payload.to_vec());
97 }
98
99 base64::engine::general_purpose::STANDARD
101 .decode(payload)
102 .map_err(|e| format!("base64 decode error: {}", e))
103}
104
105pub fn is_wave_osc(data: &[u8]) -> bool {
107 if data.len() < 9 {
108 return false;
109 }
110 data[0] == ESC && data[1] == b']' && (
111 data[2..7] == *WAVE_OSC.as_bytes() ||
112 data[2..7] == *WAVE_SERVER_OSC.as_bytes()
113 )
114}
115
116pub fn get_osc_num(data: &[u8]) -> Option<&str> {
118 if data.len() < 8 || data[0] != ESC || data[1] != b']' {
119 return None;
120 }
121 std::str::from_utf8(&data[2..7]).ok()
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn test_encode_decode_json() {
130 let json = b"{\"command\":\"test\"}";
131 let encoded = encode_wave_osc_bytes(WAVE_OSC, json).unwrap();
132
133 assert_eq!(encoded[0], ESC);
134 assert_eq!(encoded[1], b']');
135 assert_eq!(*encoded.last().unwrap(), BEL);
136 assert!(is_wave_osc(&encoded));
137
138 let decoded = decode_wave_osc_bytes(&encoded).unwrap();
139 assert_eq!(decoded, json);
140 }
141
142 #[test]
143 fn test_encode_decode_base64() {
144 let data = b"\x00\x01\x02binary data";
145 let encoded = encode_wave_osc_bytes(WAVE_OSC, data).unwrap();
146 let decoded = decode_wave_osc_bytes(&encoded).unwrap();
147 assert_eq!(decoded, data);
148 }
149
150 #[test]
151 fn test_osc_prefix() {
152 let prefix = make_osc_prefix(WAVE_OSC);
153 assert_eq!(prefix.len(), 8); assert_eq!(prefix[0], ESC);
155 }
156
157 #[test]
158 fn test_get_osc_num() {
159 let encoded = encode_wave_osc_bytes(WAVE_OSC, b"{}").unwrap();
160 assert_eq!(get_osc_num(&encoded), Some(WAVE_OSC));
161 }
162
163 #[test]
164 fn test_invalid_osc_num_length() {
165 let result = encode_wave_osc_bytes("123", b"data");
166 assert!(result.is_err());
167 }
168}